package com.istock.monitor.agent.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.springframework.util.TypeUtils;

public final class ClassUtils {
	private static final Logger LOG = Logger.getLogger(ClassUtils.class.getName());
	
	private static final String CLASS_OUTPUT_DIR = "classes";
	public static void outputClass(String className, byte[] bytes) {
		OutputStream out = null;
		try {

			File file = new File(CLASS_OUTPUT_DIR, className.replace('.', '/') + ".class");
			if (!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			out = new FileOutputStream(file);
			out.write(bytes);
			out.flush();
		} catch (Exception ex) {
			LOG.log(Level.SEVERE, "Output class bytes error. {0}", className);
			ex.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					LOG.log(Level.WARNING, "Close output stream error.");
				}
			}
		}
	}
	
	public static Class<?> transferClass(ClassLoader classLoader, byte[] bytes) {
		try {
			if (classLoader == null) {
				classLoader = ClassLoader.getSystemClassLoader();
			}
			Class<?> c = (Class<?>) defineClassMethod.invoke(classLoader,
					new Object[] { null, bytes, 0, bytes.length });
			return c;
		} catch (Exception ex) {
			LOG.log(Level.SEVERE, "Transfer class from bytes error.");
			ex.printStackTrace();
		}
		return null;
	}
	
	private static Method defineClassMethod;
	static {
		Class<?> cl = java.lang.ClassLoader.class;
		try {
			// protected final Class<?> defineClass(String name, byte[] b, int off, int len)
			// throws ClassFormatError
			defineClassMethod = cl.getDeclaredMethod("defineClass",
					new Class[] { String.class, byte[].class, int.class, int.class });
			defineClassMethod.setAccessible(true);
		} catch (Throwable ex) {
			throw new Error(ex);
		}
	}
	
	public static boolean canProxy(Class beanClass) {
//		System.out.println("============"+beanClass.getCanonicalName()+"==");
		return !beanClass.isEnum() && !Modifier.isFinal(beanClass.getModifiers()) && ConstructorUtils.getMatchingAccessibleConstructor(beanClass, null) != null
				&& !beanClass.getCanonicalName().equalsIgnoreCase("com.istock.monitor.agent.spring.DataSourceBeanPostProcessor")
				&& !beanClass.getCanonicalName().equalsIgnoreCase("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource")
				&& !beanClass.getCanonicalName().startsWith("net.sf.ehcache")
//				&& !TypeUtils.isAssignable(beanClass,org.springframework.beans.factory.FactoryBean.class)
				;
	}
}
